/*
 * @(#)NetworkRIP.java	05.09.1997  Didier Gutacker
 *
 */

import java.awt.*;
import java.util.Vector;
import java.net.URL;

/** 
 * Die Klasse NetworkRIP stellt den Kernbereich der Simulation des RIP Routing 
 * Algorithmus dar.  Hier werden alle Netzwerkkomponenten verwaltet und 
 * von hieraus werden alle Ereignisse synchronisiert.  
 * NetworkRIP hat einen eigenen Thread falls das Programm als Client läuft.  
 *
 * @author 	Didier Gutacker
 * @version     1.0, 05.09.1997
 * @see Netzwerk
 * @see NetworkRIP
 * 
 */

public final class NetworkRIP extends Netzwerk {

  private NetworkDemoRIP demo;
  public boolean noupdatesending = true; 

  /**
   * Netzwerk Konstruktor bekommt Referenzen auf das Simulationsobjekt, den Frame und
   * das Zeichenbrett übergeben (zudem noch die Breite und Höhe eines Nodes in 
   * Pixeln).
   * @see Simulation
   * @see SimulationFrame
   * @see NetzCanvas
   */

  public NetworkRIP( Simulation simulation, SimulationFrame frame, NetzCanvas canvas, int nodeWidth, int nodeHeight ) {
    super(simulation,frame,canvas,nodeWidth,nodeHeight);
  } // NetworkRIP.NetworkRIP

  public void run() {
    if ( ! frame.isClient() ) return;
    SimulationClient listener = frame.getClient();
    String e;
    Integer x = null;
    Integer y = null;
    for ( ;; ) {
      e = listener.getEvent();
      if ( e.equals( "suspend" ) ) {
	suspend();
      } else if ( e.equals( "resume" ) ) {
	resume();
      } else if ( e.equals( "finishUp" ) ) {
	finishUp();
      } else if ( e.equals( "focusComponent" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	  y = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	focussedComponent = getComponent( x.intValue(), y.intValue() );
	focussedComponent.paintRequest();
      } else if ( e.equals( "mouseMoveNotify" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	  y = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	mouseMoveNotify( (Event) null, x.intValue(), y.intValue() );
      } else if ( e.equals( "addNode" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	  y = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	addNode( x.intValue(), y.intValue() );
      } else if ( e.equals( "unmarkFocussedComponent" ) ) {
	focussedComponent.unmark();
      } else if ( e.equals( "addLink" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	  y = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	NodeRIP node1 = lookUpNode( x.intValue() );
	NodeRIP node2 = lookUpNode( y.intValue() );
	addLink( node1, node2 );
      } else if ( e.equals( "removeLink" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	removeLink( lookUpLink( x.intValue() ) );
      } else if ( e.equals( "removeNode" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	removeNode( lookUpNode( x.intValue() ) );
      } else if ( e.equals( "startSending" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
          y = new Integer( listener.getEvent() ); 
	} catch( NumberFormatException exc ) {
	} // try
        NodeRIP node1 = lookUpNode( x.intValue() );
	NodeRIP node2 = lookUpNode( y.intValue() );
	startSending(  node1,node2 );
      } else if ( e.equals( "stopSending" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	stopSending( lookUpNode( x.intValue() ) );
      } else if ( e.equals( "inspectRoutingTable" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	inspectRoutingTable( lookUpNode( x.intValue() ) );
      } else if ( e.equals( "setSpeed" ) ) {
	try {
	  x = new Integer( listener.getEvent() );
	} catch( NumberFormatException exc ) {
	} // try
	setSpeed( x.intValue() );
	frame.repaint();
      } // if
    } // for
  } // NetworkRIP.run

  NodeRIP lookUpNode( int id ) {
    for ( int i = 0; i < archComponents.size(); i += 1 ) {
      ArchitectureComponent component = (ArchitectureComponent) archComponents.elementAt( i );
      if ( component instanceof NodeRIP ) {
	if ( component.getID() == id ) {
	  return (NodeRIP) component;
	} // if
      } // if
    } // for
    return null;
  } // NetworkRIP.lookUpNode

  LinkRIP lookUpLink( int id ) {
    for ( int i = 0; i < archComponents.size(); i += 1 ) {
      ArchitectureComponent component = (ArchitectureComponent) archComponents.elementAt( i );
      if ( component instanceof LinkRIP ) {
	if ( component.getID() == id ) {
	  return (LinkRIP) component;
	} // if
      } // if
    } // for
    return null;
  } // NetworkRIP.lookUpLink

  /**
   * Reagiere auf Bewegungen des Mauszeigers.  Wird vom NetzCanvas
   * bzw. vom SimulationClient aufgerufen
   * @see SimulationClient
   * @see NetzCanvas
   */

  synchronized public final void mouseMoveNotify( Event e, int x, int y ) {
    if ( focussedComponent != null && ! linkAddRequest && ! startSendingRequest) {
      if ( frame.isServer() ) {
	frame.getServer().sendEvent( "mouseMoveNotify" );
	frame.getServer().sendEvent( Integer.toString( x ) );
	frame.getServer().sendEvent( Integer.toString( y ) );
      } // if

      int xpos = focussedComponent.getPositionX();
      int ypos = focussedComponent.getPositionY();
      int width = focussedComponent.getWidth() + 1;
      int height = focussedComponent.getHeight() + 1;
      focussedComponent.setPosition( x, y );
      int x1Clip = Math.min( xpos, x );
      int y1Clip = Math.min( ypos, y );
      int x2Clip = Math.max( xpos, x ) + width + 1;
      int y2Clip = Math.max( ypos, y ) + height + 1;
      for ( int i = 0; i < ( (NodeRIP) focussedComponent ).getLinks().size(); i += 1 ) {
	ArchitectureComponent Element = (ArchitectureComponent) ( ( NodeRIP )focussedComponent ).getLinks().elementAt( i );
	x1Clip = Math.min( Element.getPositionX(), x1Clip );
	y1Clip = Math.min( Element.getPositionY(), y1Clip );
	x2Clip = Math.max( Element.getPositionX() + Element.getWidth() + 1, x2Clip );
	y2Clip = Math.max( Element.getPositionY() + Element.getHeight() + 1, y2Clip );
      } // for
      canvas.getGraphics().clipRect( x1Clip - 1, y1Clip - 1, x2Clip + 1, y2Clip + 1 );
      canvas.getGraphics().clearRect( x1Clip - 1, y1Clip - 1, x2Clip + 1, y2Clip + 1 );
      forcedPaint();		// awt thread does all the work this time: much faster
    } // if
  } // NetworkRIP.mouseMoveNotify

  /**
   * Reagiere auf Drücken der Maustaste.  Das Ereignis wird vom NetzCanvas
   * an diese Funktion weitergeleitet.
   * @see NetzCanvas
   */

  synchronized public final void mouseDownNotify( Event e, int x, int y ) {
    ArchitectureComponent newComponent;
    if ( focussedComponent != null ) {
      newComponent = getComponent( x, y );
      if ( linkAddRequest && !focussedComponent.equals( newComponent ) ) {
	if ( !( newComponent instanceof NodeRIP ) ) {
	  return;
	} // if
	addLink( (NodeRIP) focussedComponent, (NodeRIP) newComponent );
	linkAddRequest = false;
      } // if
      if ( startSendingRequest && !focussedComponent.equals( newComponent ) ) {
	if ( !( newComponent instanceof NodeRIP ) ) {
	  return;
	} // if
	startSending( (NodeRIP) focussedComponent, ((NodeRIP) newComponent).id );
	startSendingRequest = false;
      } // if
      if ( !linkAddRequest ) {
	focussedComponent.unmark();
	if ( frame.isServer() ) {
	  frame.getServer().sendEvent( "unmarkFocussedComponent" );
	} // if
	focussedComponent.paint();
      } // if
      focussedComponent = null; // position selected
    } else {
      focussedComponent = getComponent( x, y );	// get component under current cursor
      if ( frame.isServer() ) {
	frame.getServer().sendEvent( "focusComponent" );
	frame.getServer().sendEvent( Integer.toString( x ) );
	frame.getServer().sendEvent( Integer.toString( y ) );
      } // if

      if ( focussedComponent instanceof NodeRIP ) {
	if ( nodeRemoveRequest ) {
	  removeNode( (NodeRIP) focussedComponent );
	  nodeRemoveRequest = false;
	  focussedComponent = null;
	} else if ( stopSendingRequest ) {
	  stopSendingRequest = false;
	  stopSending( (NodeRIP) focussedComponent );
	  focussedComponent = null;
	} else if ( inspectRoutingTableRequest ) {
	  inspectRoutingTableRequest = false;
	  inspectRoutingTable( (NodeRIP) focussedComponent );
	  focussedComponent = null;
	} else {
	  forcedPaintLock.acquire(); // make sure nobody else performs forcedPaint
	  focussedComponent.mark();
	  focussedComponent.paint();
	  forcedPaintLock.release();
	} // if
      } else if ( focussedComponent instanceof LinkRIP ) {
	if ( linkRemoveRequest ) {
	  removeLink( (LinkRIP) focussedComponent );
	  linkRemoveRequest = false;
	} else {
	  ((LinkRIP) focussedComponent).setWeight();
	} // if
	focussedComponent = null;
      } else {
	focussedComponent = null;
      } // if
    } // if
  } // NetworkRIP.mouseDownNotify

  /**
   * Fügt eine neue Verbindung zwischen den übergebenen Routern ein.
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see Link
   * @see NodeRIP
   * @see SimulationServer
   */

  public final LinkRIP addLink( NodeRIP node1, NodeRIP node2 ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "addLink" );
      frame.getServer().sendEvent( Integer.toString( node1.getID() ) );
      frame.getServer().sendEvent( Integer.toString( node2.getID() ) );
    } // if
    LinkRIP l = new LinkRIP( currentLinkNumber, this, canvas.getGraphics(), node1, node2);
    infinityGuard.acquire();
    infinity += 32;		// increase the net's infinity
    infinityGuard.release();
    currentLinkNumber += 1;
    archChangeLock.acquire();
    archComponents.addElement( l );
    numLinks += 1;
    archChangeLock.release();
    frame.resetButtons();
    return l;
  } // NetworkRIP.addLink

  /**
   * Entfernt die als Parameter übergebene Verbindung
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see Link
   * @see SimulationServer
   */


  public final void removeLink( LinkRIP l ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "removeLink" );
      frame.getServer().sendEvent( Integer.toString( l.getID() ) );
    } // if

    l.finishUp();
    archChangeLock.acquire();
    archComponents.removeElement( l );
    numLinks -= 1;
    archChangeLock.release();
    forcedPaint();
    infinityGuard.acquire();
    infinity -= 32;		// increase the net's infinity
    infinityGuard.release();
    frame.resetButtons();
  } // NetworkRIP.removeLink

  /**
   * Fügt einen neuen Router an den übergebenen Koordinaten dem Netz hinzu.
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   * @see NetworkRIP#removeNode
   * @see Node
   * @see SimulationServer
   */

  synchronized public final NodeRIP addNode( int x, int y ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "addNode" );
      frame.getServer().sendEvent( Integer.toString( x ) );
      frame.getServer().sendEvent( Integer.toString( y ) );
    } // if

    NodeRIP n = new NodeRIP( currentNodeNumber, this, canvas.getGraphics(), x, y, nodeWidth, nodeHeight );
    currentNodeNumber += 1;
    archChangeLock.acquire();
    archComponents.addElement( n );
    numNodes += 1;
    archChangeLock.release();
    if ( demo == null || !demo.running() ) {
      focussedComponent = n;
      n.mark();
    } // if
    n.paintRequest();
    frame.resetButtons();
    return n;
  } // NetworkRIP.addNode


  /**
   * Entfernt den als Parameter übergebenen Rechner
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see NetworkRIP#addNode
   * @see Node
   * @see SimulationServer
   */

  public final void removeNode( NodeRIP n ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "removeNode" );
      frame.getServer().sendEvent( Integer.toString( n.getID() ) );
    } // if

    n.finishUp();
    archChangeLock.acquire();
    archComponents.removeElement( n );
    numNodes -= 1;
    archChangeLock.release();
    forcedPaint();
    frame.resetButtons();
  } // NetworkRIP.removeNode

  /**
   * Starte Senden von Datenpacketen von dem als als Parameter 
   * übergebenen Rechner
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see NetworkRIP#stopSending
   * @see NodeRIP
   * @see SimulationServer
   */

  public final void startSending( NodeRIP n, int to ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "startSending" );
      frame.getServer().sendEvent( Integer.toString( n.getID() ) );
    } // if

    n.startSending(to);
    archChangeLock.acquire();
    numSenders += 1;
    archChangeLock.release();
    frame.resetButtons();
  } // NetworkRIP.startSending

  /**
   * Starte Senden von Datenpacketen von dem als als Parameter 
   * übergebenen Rechner zu dem als Parameter uebergebenen Rechner
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see NetworkRIP#stopSending
   * @see NodeRIP
   * @see SimulationServer
   */

   public final void startSending( NodeRIP n, NodeRIP m ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "startSending" );
      frame.getServer().sendEvent( Integer.toString( n.getID() ) );
    } // if
    n.startSending(m.id);
    archChangeLock.acquire();
    numSenders += 1;
    archChangeLock.release();
    frame.resetButtons();
  } // NetworkRIP.startSending
  
  /**
   * Stoppe das Senden von Datenpacketen von dem als als Parameter 
   * übergebenen Rechner
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see NetworkRIP#startSending
   * @see NodeRIP
   * @see SimulationServer
   */

  public final void stopSending( NodeRIP n ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "stopSending" );
      frame.getServer().sendEvent( Integer.toString( n.getID() ) );
    } // if

    n.stopSending();
    archChangeLock.acquire();
    numSenders -= 1;
    archChangeLock.release();
    frame.resetButtons();
  } // NetworkRIP.stopSending

  /**
   * Veranlaßt den als Parameter übergebenen Rechner ein Fenster mit seiner
   * aktuellen Leitwegtabelle zu erzeugen
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see RIPRoutingTable
   * @see NodeRIP
   * @see SimulationServer
   */

  public final void inspectRoutingTable( NodeRIP n ) {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "inspectRoutingTable" );
      frame.getServer().sendEvent( Integer.toString( n.getID() ) );
    } // if
    n.inspectTable();
  } // NetworkRIP.inspectRoutingTable

  /**
   * Veranlaßt ein komplettes Anhalten aller aktiven Netzwerkkomponenten
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see NetworkRIP#resume
   */

  public final void suspend() {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "suspend" );
    } // if
    archChangeLock.acquire();
    forcedPaintLock.acquire();
    if ( demo != null ) {
      demo.suspend();
    } // if
    for ( int i = 0; i < archComponents.size(); i += 1 ) {
      ((ArchitectureComponent)archComponents.elementAt( i )).suspend();
    } // for
    archChangeLock.release();
    forcedPaintLock.release();
  } // NetworkRIP.suspend

  /**
   * Reaktiviert alle angehaltenen Netzwerkkomponenten
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see NetworkRIP#suspend
   */

  public final void resume() {
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "resume" );
    } // if
  
    archChangeLock.acquire();
    forcedPaintLock.acquire();
    if ( demo != null ) {
      demo.resume();
    } // if
    for ( int i = 0; i < archComponents.size(); i += 1 ) {
      ((ArchitectureComponent)archComponents.elementAt( i )).resume();
    } // for
    archChangeLock.release();
    forcedPaintLock.release();
  } // NetworkRIP.resume
  
  /**
   * Beendet und löscht alle Netzwerkkomponenten
   *
   * Im Falle des Server-Betriebs wird entsprechendes Ereignis an alle
   * Clients gesendet
   *
   * THREADSAFE
   *
   * @see SimulationServer
   */

  public final void finishUp() {
    writeln( simulation.getText().networkFinishUpText ); 
    if ( frame.isServer() ) {
      frame.getServer().sendEvent( "finishUp" );
    } // if
    
    finished = true;
    for ( int i = 0; i < archComponents.size(); ) {
      if ( ( (ArchitectureComponent) archComponents.elementAt( i ) ) instanceof NodeRIP ) {
	removeNode( (NodeRIP) archComponents.elementAt( i ) );
      } else {
	removeLink( (LinkRIP) archComponents.elementAt( i ) );
      } // if
    } // for
    currentLinkNumber = 0;
    currentNodeNumber = 0;
    stopDemo();
    finished = false;
    writeln( simulation.getText().networkFinishedText ); 
  } // NetworkRIP.finishUp

  /**
   * Beendet alle Komponenten und startet die Demonstration
   *
   * THREADSAFE
   *
   * @see NetworkRIP#stopDemo
   * @see NetworkRIP#demoRunning
   */

  public final void startDemo() {
    finishUp();
    if ( demo == null ) {
      demo = new NetworkDemoRIP( this );
    } // if
    demo.start();
    frame.demoOn();
  } // NetworkRIP.startDemo

  /**
   * Beendet die Demonstration
   *
   * THREADSAFE
   *
   * @see NetworkRIP#startDemo
   * @see NetworkRIP#demoRunning
   */

  public final void stopDemo() {

    if ( demo != null ) {
      demo.stop();
    } // if
    frame.demoOff();
  } // NetworkRIP.stopDemo

  /**
   * Liefert wahr falls die Demonstration gerade aktiv ist. 
   * @see NetworkRIP#startDemo
   * @see NetworkRIP#stopDemo
   */

  public final boolean noPeriodicUpdates() {
    if ( demo != null )
      {
        return(true);
      }
    else
      return noupdatesending;
  } // NetworkRIP.noPeriodicUpdates

} // NetworkRIP











